<!--
 * Copyright ©
 * #  
 * @author: zw
 * @date: 2021-12-07 
 -->

<template>
  <input type="file" ref="uploadRef" @change="total = 0" />
  <el-button type="primary" @click="uploadFile" :disabled="total > 0 ? true : false" :loading="computedFileSize" >
    上传
  </el-button>
  <el-button @click="pauseBykeep">{{ loading ? "继续" : "暂停" }}</el-button>
  <div class="proDiv">
    <div class="divSpan">
      {{ computedFileSize ? "计算文件大小" : `上传进度：${total}%` }}
    </div>
    <el-progress :percentage="total" :status="total < 100 ? 'exception' : 'success'" />
  </div>
</template>

<script lang="js">
import { defineComponent, reactive, toRefs } from 'vue'
import axios from "axios";
import SparkMD5 from "spark-md5";
import { useInstance } from "@/common";
import { SIZE, fileParse, chunkList, formData } from "./utils";
export default defineComponent({
  name: 'displayCounter',
  setup(props, {emit, slots}) {
    const { $message: message } = useInstance();
    const state = reactive({
      uploadRef: null,
      total: 0,
      computedFileSize: false,
      loading: false,
      partList: [],
      hash: '',
      isErr: false,
      abort: false
    });
    const uploadFile = () => {
      state.computedFileSize = true;
      createChunkFile(state.uploadRef.files[0]);
    }
    //创建切片
    const createChunkFile = async (file) => {
      if (!file) return;

      // 解析为BUFFER数据
      const buffer = await fileParse(file);
      const spark = new SparkMD5.ArrayBuffer();
      spark.append(buffer);
      const hash = spark.end();
      
      // 把一个文件分割成为好几个部分（固定数量/固定大小）,每一个切片有自己的部分数据和自己的名字
      state.partList = chunkList(hash, file);
      state.hash = hash;
      getLoadingFiles(hash);
    };
    //检查当前文件是否曾经上传
    const getLoadingFiles = async (hash) => {
      const { data: { data, code }} = await axios({url: "http://localhost:8888/loadingUpload", method: 'GET', params: { hash }});
      if (code !== 0) return;

      if (data.length === 0) {
        uploadFn();
      } else {
        const p = parseInt(Math.ceil(100 / state.partList.length));
        state.total = data.length * p;
        uploadFn(data);
      }
    };
    //根据切片数创造切片数个请求
    const uploadFn = async (list) => {
      function request(formData) {
        return axios.post("http://localhost:8888/upload", formData, { headers: { "Content-Type": "multipart/form-data" } });
      }
      const uploadList = state.partList.map(item => formData(item, request));
      state.computedFileSize = false;
      const index = list ? list.length : 0;
      uploadSend(uploadList, index);
    };
    //点击暂停或者继续的函数
    const pauseBykeep = () => {
      if (!state.uploadRef.files[0]) return;
      if (state.loading) { //继续上传
        state.loading = false;
        state.abort = false;
        uploadFn();
      } else {//暂停上传
        state.loading = true;
        state.abort = true;
      }
    };
    //上传单个切片
    const uploadSend = async (uploadList, index) => {
      if (state.abort) return;// 已经中断则不再上传
      if (index >= uploadList.length) return uploadComplete();// 都传完了
      try {
        const progress = parseInt(Math.ceil(100 / state.partList.length));
        const { data: { data, code } } = await uploadList[index]();
        if (code === 2) state.isErr = true;
        if (code === 0) {
          const status = state.total + progress;
          state.partList.shift(1);
          state.total = status >= 100 ? 100 : status;
        }
      } catch (error) {
        state.loading = true;
        state.abort = true;
      }
      if (state.isErr) {
        return message.error("当前文件已上传");
      }
      index++;
      uploadSend(uploadList, index);
    };
    //最后一个切片上传完成，合并切片
    const uploadComplete = async () => {
      const {data: { data, code }} = await axios({url: "http://localhost:8888/merge", method: 'GET', params: { hash: state.hash } });
      if (code !== 0) return;
      message.success("上传成功");
    };
   return {
     uploadFile,
     pauseBykeep,
     ...toRefs(state)
    }
   }
});
</script>

<style lang="css" scoped>
.proDiv {
  margin-bottom: 10px;
}
.el-progress {
  width: 400px;
}
</style>
